/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.icanci.snow.spring.aop;

import cn.icanci.snow.spring.aop.anno.Aspect;
import cn.icanci.snow.spring.aop.anno.Order;
import cn.icanci.snow.spring.aop.aspect.AspectInfo;
import cn.icanci.snow.spring.aop.aspect.DefaultAspect;
import cn.icanci.snow.spring.core.BeanContainer;
import cn.icanci.snow.spring.util.ValidationUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author icanci
 * @date Created in 2021/05/19 17:33
 * @version 1.0
 */
public class AspectWeaver {
    private BeanContainer beanContainer;

    public AspectWeaver() {
        this.beanContainer = BeanContainer.getInstance();
    }

    public void doAop() {
        // 1.获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassByAnnotation(Aspect.class);
        // 2.拼装AspectInfo
        if (ValidationUtil.isEmpty(aspectSet)) {
            return;
        }
        List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
        // 3.遍历容器里的类
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> targetClass : classSet) {
            // 排除Aspect本身
            if (targetClass.isAnnotationPresent(Aspect.class)) {
                continue;
            }
            // 4.粗筛选复合条件的Aspect
            List<AspectInfo> roughMatchedAspectInfoList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass);
            // 5.尝试进行Aspect的织入
            wrapIfNecessary(roughMatchedAspectInfoList, targetClass);
        }
    }

    /**
     * 返回指定bean的集合
     * @param aspectSet bean集合
     * @return 返回集合
     */
    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> aspectInfoList = new ArrayList<>();
        for (Class<?> aspectClass : aspectSet) {
            if (verifyAspect(aspectClass)) {
                Order orderTag = aspectClass.getAnnotation(Order.class);
                Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
                DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass);
                //初始化表达式定位器
                PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
                AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator);
                aspectInfoList.add(aspectInfo);
            } else {
                // 不遵守规范则直接抛出异常
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfoList;
    }

    /**
     * 是否需要初步筛选
     * @param roughMatchedAspectList 集合
     * @param targetClass 目标 class
     */
    private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass) {
        if (ValidationUtil.isEmpty(roughMatchedAspectList)) {
            return;
        }
        // 创建动态代理对象
        AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList);
        Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
        beanContainer.addBean(targetClass, proxyBean);
    }

    /**
     * 粗筛
     * @param aspectInfoList aspectInfoList
     * @param targetClass targetClass
     * @return 返回筛选结果
     */
    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfoList) {
            // 粗筛
            if (aspectInfo.getPointcutLocator().roughMatches(targetClass)) {
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }

    /**
     * 框架中一定要遵循给Aspect类添加@Aspect和@Order标签的规范，同时，必须继承自 DefaultAspect.class
     * 此外，@Aspect的属性值不能是其本身
     * @param aspectClass aspectClass
     * @return 是否满足条件
     */
    private boolean verifyAspect(Class<?> aspectClass) {
        return aspectClass.isAnnotationPresent(Aspect.class) && aspectClass.isAnnotationPresent(Order.class) && DefaultAspect.class.isAssignableFrom(aspectClass);
    }
}
